/*
 * @Author: xcl
 * @Date: 2022-08-17 14:52:31
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2022-10-09 15:01:36
 * @Description: Cesium 三维地球可视化 - 自定义常用工具类
 */
CesiumGlobe = function (viewer) {
  // Cesium查看器
  this.viewer = viewer ? viewer : (window.vmCesiumViewer ? window.vmCesiumViewer : undefined)
  // 白模
  this.CurSourceShaders = []; // 临时备份，着色器 代码
  // 大型路网
  this.FlyLinesEntities = [];
  this.BusLinesEntities = [];
  this.RoadPicEntities = [];
  // 好看的点特效集合
  this.EffectsList = [];
  // Primitive模型
  this.ModelId = '';
};

/**
 * Cesium 初始化
 * @param {*} cesiumContainerId Cesium的DOM元素或ID
 * @param {*} loadingIndicatorId Cesium的loading元素或ID
 * @param {Object} opt 配置项
 * @param {Function} fun 执行函数
 * @param {*} vm 
 * @returns 
 */
CesiumGlobe.prototype.init = function (cesiumContainerId, loadingIndicatorId, opt, fun, vm) {
  let _this = this;

  window.vmCesiumViewer = undefined;

  if (!(typeof Cesium !== 'undefined')) {
    return;
  }

  if (!cesiumContainerId) {
    return;
  }

  let loadingIndicator;
  loadingIndicatorId ? loadingIndicator = document.getElementById(loadingIndicatorId) : "";
  loadingIndicator ? loadingIndicator.style.display = "block" : "";

  let viewer;
  try {
    // 使用 cesiumContainerId 在 HTML 元素中初始化 Cesium 查看器。
    viewer = new Cesium.Viewer(
      // DOM 元素或 ID
      cesiumContainerId,
      // 配置项
      Cesium.defined(opt) ? {
        // Cesium 参数调优
        scene3DOnly: opt.scene3DOnly === undefined ? (opt.debug ? false : true) : (opt.debug ? false : opt.scene3DOnly), // 如果为真，则优化3D模式的内存使用和性能，但禁用使用二维或哥伦布视图的功能
        requestRenderMode: opt.requestRenderMode === undefined ? false : opt.requestRenderMode, // 启用请求渲染模式 以减少Cesium渲染一个新帧总的时间，并且降低应用中Cesium的CPU使用率
        maximumRenderTimeChange: opt.maximumRenderTimeChange === undefined ? Infinity : opt.maximumRenderTimeChange, // 无操作时自动渲染帧率，设为数字会消耗性能，Infinity为无操作不渲染
        useBrowserRecommendedResolution: opt.useBrowserRecommendedResolution === undefined ? false : opt.useBrowserRecommendedResolution, // 是否选择浏览器推荐分辨率 如果为true，则以浏览器建议的分辨率进行渲染，并忽略 window.devicePixelRatio；设置为false，则使用window.devicePixelRatio属性
        targetFrameRate: opt.targetFrameRate === undefined ? undefined : opt.targetFrameRate, // 控制渲染帧数 fps
        maximumScreenSpaceError: opt.maximumScreenSpaceError === undefined ? 64 : opt.maximumScreenSpaceError, // 屏幕空间最大误差
        terrainShadows: opt.terrainShadows === undefined || !opt.terrainShadows ? Cesium.ShadowMode.DISABLED : Cesium.ShadowMode.ENABLED, // 地质接收阴影
        resolutionScale: opt.resolutionScale === undefined ? 1 : opt.resolutionScale, // 清晰度 0-1
        shadows: opt.shadows === undefined ? false : opt.shadows, // 是否显示光照投射的阴影
        useDefaultRenderLoop: opt.useDefaultRenderLoop === undefined ? true : opt.useDefaultRenderLoop, // 控制是否继续渲染

        // Cesium Widgets显隐
        animation: opt.animation === undefined ? false : opt.animation, // 动画
        baseLayerPicker: opt.baseLayerPicker === undefined ? false : opt.baseLayerPicker, // 图层选择
        fullscreenButton: opt.fullscreenButton === undefined ? false : opt.fullscreenButton, // 全屏
        vrButton: opt.vrButton === undefined ? false : opt.vrButton, // 双屏模式
        geocoder: opt.geocoder === undefined ? false : opt.geocoder, // 查找位置
        homeButton: opt.homeButton === undefined ? false : opt.homeButton, // 视角返回初始位置
        infoBox: opt.infoBox === undefined ? false : opt.infoBox, // 信息框
        sceneModePicker: opt.sceneModePicker === undefined ? false : opt.sceneModePicker, // 场景选择，2D/3D/哥伦布视图（2.5D)
        selectionIndicator: opt.selectionIndicator === undefined ? false : opt.selectionIndicator, // 地图选择
        timeline: opt.timeline === undefined ? false : opt.timeline, // 时间线
        navigationHelpButton: opt.navigationHelpButton === undefined ? false : opt.navigationHelpButton, // 导航帮助
        navigationInstructionsInitiallyVisible: opt.navigationInstructionsInitiallyVisible === undefined ? false : opt.navigationInstructionsInitiallyVisible, // 导航说明
        showRenderLoopErrors: opt.showRenderLoopErrors === undefined ? false : opt.showRenderLoopErrors, // 报错是否弹出错误，如果为true，则在发生渲染循环错误时，此小部件将自动向包含错误的用户显示HTML面板。

        // Cesium 设置
        shouldAnimate: opt.shouldAnimate === undefined ? false : opt.shouldAnimate, // 是否开启动画自动播放
        automaticallyTrackDataSourceClocks: opt.automaticallyTrackDataSourceClocks === undefined ? true : opt.automaticallyTrackDataSourceClocks, // 设置成true，使用公共clock对象，设置false，所有功能使用独立clock对象
        sceneMode: opt.sceneMode === undefined ? Cesium.SceneMode.SCENE3D : opt.sceneMode, // 初始化场景模式，2D/3D/哥伦布视图（2.5D)
        orderIndependentTranslucency: opt.orderIndependentTranslucency === undefined ? false : opt.orderIndependentTranslucency, // 设置渲染，如果为true且配置支持，则使用顺序无关的透明性。
        contextOptions: opt.contextOptions === undefined ? {
          webgl: {
            alpha: true,
            preserveDrawingBuffer: true,
            antialias: true,
          },
        } : {}, // 创建场景时，配置webgl
        skyBox: opt.skyBox === undefined ? false : opt.skyBox, // 配置天空盒子或不显示天空盒子(false)，设置为undefined，采用默认天空盒子
        skyAtmosphere: opt.skyAtmosphere === undefined ? false : opt.skyAtmosphere, // 配置大气或不显示大气
        fullscreenElement: opt.fullscreenElement === undefined ? '' : opt.fullscreenElement, // 配置全屏按钮，传入id或者dom
        sun: opt.sun === undefined ? false : opt.sun, // 太阳
        moon: opt.moon === undefined ? false : opt.moon, // 月亮
      } : {}
    );

    // Cesium 执行函数
    fun(viewer);
  } catch (exception) {
    loadingIndicator ? loadingIndicator.style.display = "none" : "";
    const message = Cesium.formatError(exception);
    opt.debug ? console.error(message) : "";
    if (!document.querySelector(".cesium-widget-errorPanel")) {
      // eslint-disable-next-line no-alert
      const h = vm.$createElement;
      vm.$message({
        message: h('div', null, [
          h('p', {
            style: 'color: #F56C6C;font-weight: 600;line-height: 28px;font-size: 18px;',
          }, 'formatError：'),
          h('p', {
            style: 'color: #F56C6C;font-weight: 400;line-height: 16px;font-size: 14px;',
          }, message)
        ]),
        type: 'error',
        duration: 5000,
        offset: 100,
        showClose: true,
      });
    }
    return;
  }

  if (!Cesium.defined(opt)) {
    return;
  }

  // 取消双击默认效果
  viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
    Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
  );
  viewer.extend(Cesium.viewerDragDropMixin); // 添加 基本的拖放功能
  if (opt.inspector) {
    // 添加 Cesium的Inspector面板，可以显示Primitive的坐标轴、边界球等信息，还可以显示网格坐标系信息等
    viewer.extend(Cesium.viewerCesiumInspectorMixin);
  }

  const showLoadError = function (name, error) {
    const title = `加载文件时出错：${name}`;
    const message =
      "加载文件时出错，可能表明该文件无效。 详细的错误报告如下：";
    // viewer.cesiumWidget.showErrorPanel(title, message, error);
    const h = vm.$createElement;
    vm.$message({
      message: h('div', null, [
        h('p', {
          style: 'color: #F56C6C;font-weight: 600;line-height: 28px;font-size: 18px;',
        }, title),
        h('p', {
          style: 'color: #F56C6C;font-weight: 400;line-height: 16px;font-size: 14px;',
        }, message)
      ]),
      type: 'error',
      duration: 5000,
      offset: 100,
      showClose: true,
    });
  };

  // 为每一帧添加监听事件
  viewer.clock.onTick.addEventListener(function (e) {
    // opt.debug ? console.log(e) : "";
  });
  // 如果我们在处理丢弃的文件时遇到错误，则显示弹出警报
  viewer.dropError.addEventListener(function (viewerArg, name, error) {
    showLoadError(name, error);
  });
  viewer.scene.renderError.addEventListener(function (error) {
    opt.debug ? console.log(error) : "";
    // 如果模型加载失败，请重新启动渲染循环。
    if (viewer) {
      viewer.dataSources.removeAll(true);
      viewer.scene.primitives.removeAll();
      viewer.entities.removeAll();
      viewer.imageryLayers.removeAll();
    }
    viewer.useDefaultRenderLoop = true;
  });

  const scene = viewer.scene;
  if (opt.fps) {
    scene.debugShowFramesPerSecond = true;
  }

  if (Cesium.defined(opt.view)) {
    const view = opt.view;
    const splitQuery = view.split(/[ ,]+/);
    if (splitQuery.length > 1) {
      const longitude = !isNaN(+splitQuery[0]) ? +splitQuery[0] : 0.0;
      const latitude = !isNaN(+splitQuery[1]) ? +splitQuery[1] : 0.0;
      const height =
        splitQuery.length > 2 && !isNaN(+splitQuery[2]) ?
        +splitQuery[2] :
        300.0;
      const heading =
        splitQuery.length > 3 && !isNaN(+splitQuery[3]) ?
        Cesium.Math.toRadians(+splitQuery[3]) :
        undefined;
      const pitch =
        splitQuery.length > 4 && !isNaN(+splitQuery[4]) ?
        Cesium.Math.toRadians(+splitQuery[4]) :
        undefined;
      const roll =
        splitQuery.length > 5 && !isNaN(+splitQuery[5]) ?
        Cesium.Math.toRadians(+splitQuery[5]) :
        undefined;

      viewer.camera.setView({
        destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
        orientation: {
          heading: heading,
          pitch: pitch,
          roll: roll,
        },
      });
    }
  }


  const camera = viewer.camera;

  function saveCamera() {
    const position = camera.positionCartographic;
    let hpr = "";
    if (Cesium.defined(camera.heading)) {
      hpr = `,${Cesium.Math.toDegrees(camera.heading)},${Cesium.Math.toDegrees(
        camera.pitch
      )},${Cesium.Math.toDegrees(camera.roll)}`;
    }
    const view = `${Cesium.Math.toDegrees(
      position.longitude
    )},${Cesium.Math.toDegrees(position.latitude)},${position.height}${hpr}`;
    console.log('摄像机当前位置参数：', view)
  }

  let timeout;
  if (opt.saveCamera) {
    camera.changed.addEventListener(function () {
      window.clearTimeout(timeout);
      timeout = window.setTimeout(saveCamera, 1000);
    });
  }

  loadingIndicator ? loadingIndicator.style.display = "none" : "";

  _this.viewer = viewer;
  // 挂载到window对象上
  window.vmCesiumViewer = viewer
  // 打印输出
  opt.debug ? console.log('window.vmCesiumViewer：', window.vmCesiumViewer) : "";
  opt.debug ? console.log('CesiumGlobeData：', CesiumGlobeData) : "";
  opt.debug ? console.log('new CesiumGlobe()：', new CesiumGlobe()) : "";
}

/**
 * Cesium 其它设置
 * @param {*} viewer 
 * @param {Object} opt 配置项
 * @param {*} vm 
 */
CesiumGlobe.prototype.otherSettings = function (viewer, opt, vm) {
  // 去除商标、logo
  viewer._cesiumWidget._creditContainer.style.display = "none";

  // 设置场景背景色透明 只有在没有天空框时才可见，即，场景#skyBox未定义。
  viewer.scene.backgroundColor = Cesium.Color.TRANSPARENT;

  // 设置球体globe的颜色，在没有可用图像时，获取或设置全局的颜色，默认值：rgba(27, 126, 167, 1)
  viewer.scene.globe.baseColor = Cesium.defined(opt) && opt.globeBaseColor &&
    Cesium.defined(opt.globeBaseColor.r) &&
    Cesium.defined(opt.globeBaseColor.g) &&
    Cesium.defined(opt.globeBaseColor.b) &&
    Cesium.defined(opt.globeBaseColor.a) ? new Cesium.Color(
      opt.globeBaseColor.r / 255,
      opt.globeBaseColor.g / 255,
      opt.globeBaseColor.b / 255,
      opt.globeBaseColor.a
    ) : new Cesium.Color(
      9 / 255,
      25 / 255,
      51 / 255,
      1
    );

  // 关闭地下模式
  viewer.scene.undergroundMode = Cesium.defined(opt) && opt.undergroundMode === undefined ? false : opt.undergroundMode;

  // 鼠标滚轮放大的步长参数
  viewer.scene.screenSpaceCameraController._zoomFactor = Cesium.defined(opt) && opt._zoomFactor === undefined ? 5.0 : opt._zoomFactor;

  // 开启深度检测(在深度检测不开启时，拾取的坐标会不准确)
  viewer.scene.globe.depthTestAgainstTerrain = true;

  // 是否显示地球globe
  viewer.scene.globe.show = Cesium.defined(opt) && opt.isGlobe === undefined ? true : opt.isGlobe;

  // 是否开启抗锯齿
  if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
    // 判断是否支持图像渲染像素化处理
    // viewer.resolutionScale = window.devicePixelRatio;
    var vtxf_dpr = window.devicePixelRatio;
    // 适度降低分辨率
    while (vtxf_dpr >= 2.0) {
      vtxf_dpr /= 2.0;
    }
    viewer.resolutionScale = vtxf_dpr;
  }
  viewer.scene.fxaa = true;
  viewer.scene.postProcessStages.fxaa.enabled = true;

  // 解决Cesium显示画面模糊的问题(性能优化，适度降低分辨率)
  viewer._cesiumWidget._supportsImageRenderingPixelated =
    Cesium.FeatureDetection.supportsImageRenderingPixelated();
  viewer._cesiumWidget._forceResize = true;
  if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
    var vtxf_dpr = window.devicePixelRatio;
    // 适度降低分辨率
    while (vtxf_dpr >= 2.0) {
      vtxf_dpr /= 2.0;
    }
    viewer.resolutionScale = vtxf_dpr;
  }
}

/**
 * 增加 所有配置图层
 * @param {*} viewer 
 * @param {Array} MapImageryList 图层资源数据
 * @param {*} vm 
 * @returns 
 */
CesiumGlobe.prototype.setConfigMapList = function (viewer, MapImageryList, vm) {
  let _this = this;

  if (!(MapImageryList && Array.isArray(MapImageryList) && MapImageryList.length > 0)) {
    return;
  }

  const imageryLayers = viewer.imageryLayers;

  // 先清空所有
  // imageryLayers.removeAll(true);
  CesiumGlobeData.imageryLayers = [];

  MapImageryList.some((elem, index) => {
    if (index === 0) {
      return false
    }
    imageryLayers.addImageryProvider(_this.setOneimageryProvider(elem));
  });
  // 设置具体的 ImageryLayer 参数
  MapImageryList.some((elem, index) => {
    console.log(viewer.imageryLayers)
    const baseLayer = viewer.imageryLayers.get(index);
    if (!baseLayer) {
      return;
    }
    if (elem.interfaceConfig) {
      Object.getOwnPropertyNames(elem.interfaceConfig).forEach(function (
        key
      ) {
        baseLayer[key] = elem.interfaceConfig[key];
      })
    }
    // 设置 滤镜效果
    baseLayer.invertColor = elem.invertswitch;
    baseLayer.filterRGB = [255.0, 255.0, 255.0];
    if (elem.filterRGB !== '#000000' && elem.filterRGB !== '#ffffff') {
      baseLayer.filterRGB = colorRgb(elem.filterRGB);
    }

    // 设置 offset 偏移量
    const offset = elem.offset.split(',');
    if (offset.length === 2) {
      try {
        const oxy = [
          parseFloat(offset[0]),
          parseFloat(offset[1]),
        ];
        setTimeout(() => {
          let i = baseLayer.imageryProvider
          if (!i) {
            return false;
          }
          let _rectangleNortheastInMeters = i.tilingScheme._rectangleNortheastInMeters;
          _rectangleNortheastInMeters.x += oxy[0];
          _rectangleNortheastInMeters.y += oxy[1];
        }, 2000);
      } catch (error) {
        console.log(error);
      }
    }

    // 更改cesium的着色器代码 关于滤镜和反色的 [在不更改cesium源文件的情况下]
    _this.changeImageryProviderColors(viewer, baseLayer);

    // 显隐
    _this.isShowOneImagery(elem.isShow, baseLayer);
    // 绑定数据
    CesiumGlobeData.imageryLayers.push({
      layer: baseLayer.imageryProvider, // 即：ImageryProvider
      imagery: baseLayer, // 即：baseImageryLayer
    })
  })
}

/**
 * 增加 一个配置图层
 * @param {*} MapImagery 
 * @returns 
 */
CesiumGlobe.prototype.setOneimageryProvider = function (MapImagery) {
  if (MapImagery.classConfig.customTags) {
    MapImagery.classConfig.customTags = evil(
      '(' + MapImagery.classConfig.customTags + ')'
    )
  }
  return new Cesium[MapImagery.type](MapImagery.classConfig)
}

/**
 * 更改 cesium 着色的方法
 * @param {*} viewer 
 * @param {*} baseLayer 影像图层
 */
CesiumGlobe.prototype.changeImageryProviderColors = function (viewer, baseLayer) {
  // 更改底图的着色器 代码
  const baseFragmentShaderSource =
    viewer.scene.globe._surfaceShaderSet.baseFragmentShaderSource.sources
  for (let i = 0; i < baseFragmentShaderSource.length; i++) {
    const oneSource = baseFragmentShaderSource[i]
    // 格式必须一致 不能多有空格 且保持版本一致性
    const strS = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
    let strT = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
    if (baseLayer.invertColor) {
      strT += `
        color.r = 1.0 - color.r;
        color.g = 1.0 - color.g;
        color.b = 1.0 - color.b;
      `
      strT += `
      color.r = color.r * ${baseLayer.filterRGB[0]}.0/255.0;
      color.g = color.g * ${baseLayer.filterRGB[1]}.0/255.0;
      color.b = color.b * ${baseLayer.filterRGB[2]}.0/255.0;
      `
    }

    if (oneSource.indexOf(strS) !== -1) {
      baseFragmentShaderSource[i] = baseFragmentShaderSource[i].replace(
        strS,
        strT
      )
    }
  }
}

/**
 * 增加 所有影像图层
 * @param {*} viewer 
 * @param {*} mapImagery 在线瓦片地图服务资源
 * @param {Array} data 影像图层数据
 * @param {*} vm 
 */
CesiumGlobe.prototype.imageryFunction = function (viewer, mapImagery, data, vm) {
  let _this = this;

  if (!(data && Array.isArray(data) && data.length > 0)) {
    return;
  }

  var layerCollection = viewer.imageryLayers;

  // 先清空所有
  layerCollection.removeAll(true);
  CesiumGlobeData.imageryLayers = [];

  // 添加
  var imageryLayers = [];
  data.forEach(element => {
    imageryLayers.push({
      imageryLayer: _this.addOneImagery(
        viewer,
        mapImagery,
        element.type,
        element.name
      ),
      isShow: element.isShow
    })
  });

  // 显隐 和 绑定数据
  if (imageryLayers.length == 0) {
    return;
  }
  imageryLayers.forEach(item => {
    // 显隐
    if (item.imageryLayer && item.imageryLayer.imagery) {
      _this.isShowOneImagery(item.isShow, item.imageryLayer.imagery);
    }
    // 绑定数据
    CesiumGlobeData.imageryLayers.push(item.imageryLayer)
  });
};

/**
 * 增加 一个影像图层
 * @param {*} viewer 
 * @param {*} mapImagery 在线瓦片地图服务资源
 * @param {String} type 类型: Baidu/Amap/Tdt/Tencent/ArcGIS
 * @param {String} layerName 图层在mapImagery中的名称/key
 * @returns 
 */
CesiumGlobe.prototype.addOneImagery = function (viewer, mapImagery, type, layerName) {
  var layer, imagery;
  if (type == "Baidu") {
    // Cesium加载百度地图
    layer = new Cesium.BaiduImageryProvider({
      mapImage: mapImagery[layerName], // 默认为：百度地图-矢量底图
      crs: "WGS84", // 使用84坐标系，默认为：BD09
    });
  } else if (type == "Amap") {
    // Cesium加载高德地图
    layer = new Cesium.AmapImageryProvider({
      mapImage: mapImagery[layerName], // 默认为：高德地图-矢量底图
      crs: "WGS84", // 使用84坐标系，默认为：BD09
    });
  } else if (type == "Tdt") {
    // Cesium加载天地图
    layer = new Cesium.TdtImageryProvider({
      mapImage: mapImagery[layerName], // 默认为：天地图-矢量底图
      token: "823eaf1fabd67df98a799ae929a62f48", // 令牌
    });
  } else if (type == "Tencent") {
    // Cesium加载腾讯地图
    layer = new Cesium.TencentImageryProvider({
      mapImage: mapImagery[layerName], // 默认为：腾讯地图-电子图经典风格
      isImage: false, // 是否为影像
    });
  } else if (type == "ArcGIS") {
    // Cesium加载ArcGIS
    layer = new Cesium.ArcGISImageryProvider({
      mapImage: mapImagery[layerName], // 默认为：ArcGIS-经典地图
    });
  } else {
    layer = null;
  }
  var layerCollection = viewer.imageryLayers;
  if (layer) {
    imagery = layerCollection.addImageryProvider(layer);
  } else {
    imagery = null;
  }
  return imagery && layer ? {
      layer: layer, // 即：ImageryProvider
      imagery: imagery, // 即：baseImageryLayer
    } :
    null;
};

/**
 * 显示或者隐藏Imagery
 * @param {Boolean} isShow 
 * @param {*} imagery 
 */
CesiumGlobe.prototype.isShowOneImagery = function (isShow, imagery) {
  imagery.show = isShow;
};

/**
 * 初始化 cesium-navigation 控件
 * @param {*} viewer 
 * @param {*} CesiumNavigation 插件：cesium-navigation-es6
 */
CesiumGlobe.prototype.initCesiumNavigation = function (viewer, CesiumNavigation) {
  const options = {}
  // 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和 Cesium.Rectangle.
  options.defaultResetView = new Cesium.Rectangle.fromDegrees(80, 22, 130, 50) // new Cesium.Rectangle.fromDegrees(113.8980, 22.4899, 113.9686, 22.5438)
  // 用于启用或禁用罗盘。true是启用罗盘，false是禁用罗盘。默认值为true。如果将选项设置为false，则罗盘将不会添加到地图中。
  options.enableCompass = true
  // 用于启用或禁用缩放控件。true是启用，false是禁用。默认值为true。如果将选项设置为false，则缩放控件将不会添加到地图中。
  options.enableZoomControls = true
  // 用于启用或禁用距离图例。true是启用，false是禁用。默认值为true。如果将选项设置为false，距离图例将不会添加到地图中。
  options.enableDistanceLegend = true
  // 用于启用或禁用指南针外环。true是启用，false是禁用。默认值为true。如果将选项设置为false，则该环将可见但无效。
  options.enableCompassOuterRing = true
  CesiumNavigation(viewer, options)
}

/**
 * 获取当前视图的中心经纬度
 * @param {*} viewer 
 * @returns 
 */
CesiumGlobe.prototype.getCurCenterlonLat = function (viewer) {
  let result = viewer.camera.pickEllipsoid(
    new Cesium.Cartesian2(
      viewer.canvas.clientWidth / 2,
      viewer.canvas.clientHeight / 2
    )
  )
  let curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(result)
  let lon = (curPosition.longitude * 180) / Math.PI
  let lat = (curPosition.latitude * 180) / Math.PI
  return {
    lon: lon,
    lat: lat,
  }
}

/**
 * 加载所有 3D 白模
 * @param {*} viewer 
 * @param {Array} datas 所有需要展示的白模数据
 * @param {*} vm 
 */
CesiumGlobe.prototype.titlesetList = function (viewer, datas, vm) {
  let _this = this;

  // 先清空集合
  // viewer.scene.primitives.removeAll();
  CesiumGlobeData._3dTitlesetList = [];

  if (datas) {
    datas.forEach((element, index) => {
      if (element.url) {
        _this.addOne3dTitleset(viewer, element, vm, index)
      }
    })
  }
}

/**
 * 增加一个 3D 白模
 * @param {*} viewer 
 * @param {Object} ele 某一个3D白模的数据
 * @param {*} index 
 * @param {*} vm 
 * @param {Boolean} isConfig 
 */
CesiumGlobe.prototype.addOne3dTitleset = function (viewer, ele, vm, index, isConfig = false) {
  let _this = this;

  const modalOne = viewer.scene.primitives.add(
    new Cesium.Cesium3DTileset({
      url: ele.url,
      show: true,

      // 3dtiles姿态，包括位置，旋转角度，高度；也可以通过 update3dtilesMaxtrix 设置
      // modelMatrix: Cesium.Matrix4.fromArray([
      //   1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
      // ]),

      /** 参数设置 */
      shadows: 4,
      maximumScreenSpaceError: 1, // Temporary workaround for low memory mobile devices - Increase maximum error to 8.
      maximumNumberOfLoadedTiles: 1000, // Temporary workaround for low memory mobile devices - Decrease (disable) tile cache.

      /** 优化加载，提升流畅度 */
      skipLevelOfDetail: true,
      baseScreenSpaceError: 1024,
      maximumScreenSpaceError: 256, // 数值加大，能让最终成像变模糊
      skipScreenSpaceErrorFactor: 16,
      skipLevels: 1,
      immediatelyLoadDesiredLevelOfDetail: false,
      loadSiblings: true, // 如果为true则不会在已加载完概况房屋后，自动从中心开始超清化房屋
      cullWithChildrenBounds: true,
      cullRequestsWhileMoving: true,
      cullRequestsWhileMovingMultiplier: 10, // 值越小能够更快的剔除
      preloadWhenHidden: true,
      preferLeaves: true,
      maximumMemoryUsage: 128, // 内存分配变小有利于倾斜摄影数据回收，提升性能体验
      progressiveResolutionHeightFraction: 0.5, // 数值偏于0能够让初始加载变得模糊
      dynamicScreenSpaceErrorDensity: 0.1, // 数值加大，能让周边加载变快
      dynamicScreenSpaceErrorFactor: 1,
      dynamicScreenSpaceError: true, // 有了这个后，会在真正的全屏加载完之后才清晰化房屋
      backFaceCulling: false, //背面绘制

      /** 调试 */
      // debugShowBoundingVolume: true,
      // debugColorizeTiles: true,
      // debugShowUrl: true,
      // debugShowContentBoundingVolume: true,
      // debugShowViewerRequestVolume: true,
      // debugShowRenderingStatistics: true,
      // debugShowMemoryUsage: true,
    })
  )

  // 开始处理
  modalOne.readyPromise.then(function (tileset) {
    vm ? vm.loadingText = "开始载入3D白模" : "";
    vm ? vm.loading = true : "";

    // modalOne.show = true;// 显隐

    if (ele.flytoswitch === 1) {
      viewer.zoomTo(tileset) // 摄像头切到到白模的位置
    }

    // 白模的 更改3dtiles姿态，包括位置，旋转角度，高度
    _this.update3dtilesMaxtrix(tileset, ele)

    // 设置白模的颜色
    tileset.style = new Cesium.Cesium3DTileStyle({
      color: {
        conditions: [
          ['true', `color('${ele.color}')`]
        ],
        // show: "",
        meta: {
          description: '',
        },
      }
    })

    // 设置白模的打光效果
    if (ele.effectswitch === 1 || isConfig) {
      _this.makeEffect(tileset, ele, index, isConfig)
      // _this.customShader(modalOne);
    }
  }).catch(function (error) {
    console.log(error);
  });

  // 处理中...
  modalOne.loadProgress.addEventListener(function (
    numberOfPendingRequests,
    numberOfTilesProcessing
  ) {
    vm ? vm.loading = true : "";
    if (numberOfPendingRequests === 0 && numberOfTilesProcessing === 0) {
      vm ? vm.loadingText =
        "载入3D白模中...，待处理: 0个, 已处理: 0个" : "";
      return;
    }
    vm ? vm.loadingText =
      "载入3D白模中...，待处理: " +
      numberOfPendingRequests +
      "个, 已处理: " +
      numberOfTilesProcessing +
      "个" : "";
  });

  // 处理完成
  modalOne.allTilesLoaded.addEventListener(function () {
    vm ? vm.loadingText = "3D白模已载入完成" : "";
    vm ? vm.loading = false : "";
  });

  // 加载完成
  modalOne.initialTilesLoaded.addEventListener(function () {
    vm ? vm.loadingText = "3D白模已载入完成" : "";
    // vm ? vm.loading = false : "";
  });

  // 触发事件以指示已加载tile的内容
  modalOne.tileLoad.addEventListener(function (tile) {
    // console.log("加载了一个b3dm：", tile._header.content.uri);
  });

  // 指定颜色如何与模型混合 HIGHLIGHT、REPLACE、MIX
  tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.HIGHLIGHT;

  // 绑定数据
  CesiumGlobeData._3dTitlesetList.push(modalOne);
}

/**
 * 显示或隐藏某个 3D 白模
 * @param {*} tileset
 * @param {Boolean} val 
 */
CesiumGlobe.prototype.isShowOne3dTitleset = function (tileset, val) {
  tileset.show = val;
}

/**
 * 设置某个 3D 白模的偏移
 * @param {Object} ele 某一个3D白模的数据
 */
CesiumGlobe.prototype.setOneModalOffset = function (ele) {
  const OneModal = CesiumGlobeData._3dTitlesetList[0]
  if (OneModal) {
    this.update3dtilesMaxtrix(OneModal, ele)
  }
}

/**
 * 更改某个 3D 白模的姿态，包括位置，旋转角度，高度
 * @param {*} tileset
 * @param {Object} ele 某一个3D白模的数据
 * @returns 
 */
CesiumGlobe.prototype.update3dtilesMaxtrix = function (tileset, ele) {
  if (!tileset.ready) {
    return
  }
  // 根据tileset的边界球体中心点的笛卡尔坐标得到经纬度坐标
  const cartographic = Cesium.Cartographic.fromCartesian(
    tileset.boundingSphere.center
  )
  // 根据经纬度和高度0，得到地面笛卡尔坐标
  const surface = Cesium.Cartesian3.fromRadians(
    cartographic.longitude,
    cartographic.latitude,
    cartographic.height
  )
  // 根据经纬度和需要的高度，得到偏移后的笛卡尔坐标
  const offset = Cesium.Cartesian3.fromRadians(
    cartographic.longitude + Cesium.Math.toRadians(ele.offset_x), // 这里更改的是经纬度偏移
    cartographic.latitude + Cesium.Math.toRadians(ele.offset_y),
    cartographic.height + ele.offset_z // 程度的高度 需要偏移
  )
  // 计算坐标变换，得到新的笛卡尔坐标
  const translation = Cesium.Cartesian3.subtract(
    offset,
    surface,
    new Cesium.Cartesian3()
  )
  // 调整3dtiles位置
  tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation)
}

/**
 * 设置某个 3D 白模的打光效果
 * @param {*} tileset
 * @param {Object} ele 某一个3D白模的数据
 * @param {*} index 
 * @param {Boolean} isConfig 
 */
CesiumGlobe.prototype.makeEffect = function (tileset, ele, index, isConfig) {
  const _this = this
  // 批处理可见的3D白模
  tileset.tileVisible.addEventListener(
    _this.tileVisible_addEventListener_fun, {
      curEleDatas: window.CesiumGlobeConfig.tileset,
      ele: ele,
      index: index,
      CurSourceShaders: _this.CurSourceShaders,
      isConfig: isConfig
    }
  )
}
CesiumGlobe.prototype.tileVisible_addEventListener_fun = function (cesium3DTile) {
  const v_this = this;
  const ele = window.CesiumGlobeConfig.tileset[v_this.index]
  // 以下设置白模的打光效果
  const cesium3DTileCon = cesium3DTile.content
  const featuresLength = cesium3DTileCon.featuresLength
  const effect_color = colorRgb1(ele.effect_color)
  for (let i = 0; i < featuresLength; i += 2) {
    const _model = cesium3DTileCon.getFeature(i).content._model
    if (_model && _model._sourcePrograms && _model._rendererResources) {
      Object.getOwnPropertyNames(_model._sourcePrograms).forEach(function (
        i
      ) {
        const msp = _model._sourcePrograms[i]
        // 备份着色器 代码
        if (!v_this.CurSourceShaders[v_this.index]) {
          v_this.CurSourceShaders[v_this.index] = _model._rendererResources.sourceShaders[msp.fragmentShader]
        }
        if (ele.effectswitch !== 1) {
          _model._rendererResources.sourceShaders[msp.fragmentShader] = v_this.CurSourceShaders[v_this.index]
        } else {
          _model._rendererResources.sourceShaders[msp.fragmentShader] = `
          varying vec3 v_positionEC;
          void main(void){
            vec4 position = czm_inverseModelView * vec4(v_positionEC,1); // 位置
            float glowRange = ${ele.height.toFixed(2)}; // 光环的移动范围(高度)
            gl_FragColor = vec4(${effect_color[0]}, ${effect_color[1]}, ${effect_color[2]}, 1.0); // 颜色
            // 底楼 亮度太暗了，那么把20%以内的底楼，都不再变暗
            if((position.z / 100.0) < 0.2) {
              gl_FragColor *= vec4(vec3(position.z / 100.0 * 2.0), 1.0);
            }else{
              gl_FragColor *= vec4(vec3(position.z / 100.0), 1.0); // 渐变
            }
            // 动态光环
            float time = fract(czm_frameNumber / 360.0);
            time = abs(time - 0.5) * 2.0;
            float diff = step(0.005, abs( clamp(position.z / glowRange, 0.0, 1.0) - time));
            gl_FragColor.rgb += gl_FragColor.rgb * (1.0 - diff);
          }
          `
        }
      })
      _model._shouldRegenerateShaders = ele.effectswitch === 1 || v_this.isConfig // 控制 true
    }
  }
}

/**
 * 某个 3D 白模的建筑泛光特效 (有BUG，禁止使用)
 * @param {*} tileset
 */
CesiumGlobe.prototype.customShader = function (tileset) {
  // 建筑泛光特效
  var customShader = new Cesium.CustomShader({
    lightingModel: Cesium.LightingModel.UNLIT,
    fragmentShaderText: `
      void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
          // 注意shader中写浮点数是，一定要带小数点，否则会报错，比如0需要写成0.0，1要写成1.0
          float _baseHeight = 0.0; // 物体的基础高度，需要修改成一个合适的建筑基础高度
          float _heightRange = 21.0; // 高亮的范围(_baseHeight ~ _baseHeight + _      heightRange) 默认是 0-60米
          float _glowRange = 100.0; // 光环的移动范围(高度)
          float vtxf_height = fsInput.attributes.positionMC.z-_baseHeight;
          float vtxf_a11 = fract(czm_frameNumber / 120.0) * 3.14159265 * 2.0;
          float vtxf_a12 = vtxf_height / _heightRange + sin(vtxf_a11) * 0.1;
          material.diffuse*= vec3(vtxf_a12, vtxf_a12, vtxf_a12);
          float vtxf_a13 = fract(czm_frameNumber / 360.0);
          float vtxf_h = clamp(vtxf_height / _glowRange, 0.0, 1.0);
          vtxf_a13 = abs(vtxf_a13 - 0.5) * 2.0;
          float vtxf_diff = step(0.005, abs(vtxf_h - vtxf_a13));
          material.diffuse += material.diffuse * (1.0 - vtxf_diff);
      }       
    `,
  });
  // 因为customShader属于实验阶段的功能，为防止变化，请启用启用CesiumJS的ExperimentalFeatures
  Cesium.ExperimentalFeatures.enableModelExperimental = true;
  // 自定义着色器，应用于3D瓦片集中的所有3D瓦片
  tileset.customShader = customShader;
}

/**
 * 大型路网（飞线 + 交通线 + 道路穿梭线）特效
 * @param {*} viewer 
 * @param {*} turf 地理空间分析库，处理各种地图算法
 * @param {Array} datas 需要展示的数据
 * @param {*} vm 
 */
CesiumGlobe.prototype.roadNetwork = function (viewer, turf, datas, vm) {
  const _this = this
  if (datas) {
    datas.forEach((element, index) => {
      let setup_param = JSON.parse(element.setup_param)
      for (let key in setup_param) {
        if (key !== 'color') {
          setup_param[key] = parseFloat(setup_param[key])
        }
      }
      if (element.type === 'FlyLines') {
        this.addFlyLines(viewer, turf, [setup_param.startPoint_lng, setup_param.startPoint_lat, setup_param.endPoint_ing, setup_param.endPoint_lat], setup_param.color, element.width, setup_param.height, setup_param.speed, setup_param.percent, setup_param.gradient, setup_param.random)
      } else if (element.type === 'BusLines') {
        this.addBusLines(viewer, element.geojsonfile,
          setup_param.color, element.width, setup_param.speed, setup_param.percent, setup_param.gradient
        )
      } else if (element.type === 'RoadPic') {
        this.addRoadPic(viewer, element.geojsonfile, element.effectimage, element.width, setup_param.time)
      }
    })
  }
}

/**
 * 新增飞线
 * @param {*} viewer 
 * @param {*} turf 地理空间分析库，处理各种地图算法
 * @param {*} bbox [起始lon经度,起始lat纬度,终止lon经度,终止lat纬度]
 * @param {*} color 效果颜色
 * @param {*} width 宽度
 * @param {*} height 最大高度
 * @param {*} speed 速度
 * @param {*} percent 拖尾长
 * @param {*} gradient 变化率
 * @param {*} random 飞线数/条
 */
CesiumGlobe.prototype.addFlyLines = function (viewer, turf, bbox, color, width, height, speed, percent, gradient, random) {
  const _this = this;
  // 生成随机点
  let points = turf.randomPoint(random, {
    bbox: bbox,
  })
  let features = points.features
  let point;
  let startPosition;
  let endPosition;
  features.forEach((item) => {
    point = item.geometry.coordinates
    startPosition = Cesium.Cartesian3.fromDegrees(point[0], point[1], 0)
    endPosition = Cesium.Cartesian3.fromDegrees(
      point[0],
      point[1],
      height * Math.random()
    )
    _this.FlyLinesEntities.push(viewer.entities.add({
      polyline: {
        positions: [startPosition, endPosition],
        width: width,
        material: new Cesium.PolylineTrailMaterialProperty({
          speed: speed * Math.random(),
          color: new Cesium.Color.fromCssColorString(color),
          percent: percent, // 尾巴拖多少长
          gradient: gradient, // 变化率
        }),
      },
    }))
  })
}

/**
 * 清空飞线
 * @param {*} viewer 
 */
CesiumGlobe.prototype.clearFlyLines = function (viewer) {
  const _this = this
  this.FlyLinesEntities.forEach((item) => {
    viewer.entities.removeById(item.id)
  })
  this.FlyLinesEntities = []
}

/**
 * 新增交通线
 * @param {*} viewer 
 * @param {*} url 
 * @param {*} color 效果颜色
 * @param {*} width 宽度
 * @param {*} speed 速度
 * @param {*} percent 拖尾长
 * @param {*} gradient 变化率
 */
CesiumGlobe.prototype.addBusLines = function (viewer, url, color, width, speed, percent, gradient) {
  const _this = this
  let promise = Cesium.GeoJsonDataSource.load(url)
  promise.then(function (dataSource) {
    viewer.dataSources.add(dataSource)
    _this.BusLinesEntities = dataSource.entities.values
    let del_list = []
    for (let i = 0; i < _this.BusLinesEntities.length; i++) {
      const entity = _this.BusLinesEntities[i]
      if (entity.polyline.positions._value.length > 6) { // 这里把街道复杂度小余6的进行筛除
        entity.polyline.width = width
        entity.polyline.material = new Cesium.PolylineTrailMaterialProperty({
          speed: speed * Math.random(),
          color: new Cesium.Color.fromCssColorString(color),
          percent: percent, // 尾巴拖多少长
          gradient: gradient, // 变化率
        })
      } else {
        del_list.push(entity.id)
      }
    }
    del_list.forEach((id) => {
      dataSource.entities.removeById(id)
    })
  })
}

/**
 * 新增道路穿梭线
 * @param {*} viewer 
 * @param {*} url 
 * @param {*} Picurl 
 * @param {*} width 宽度
 * @param {*} time 延迟时间
 */
CesiumGlobe.prototype.addRoadPic = function (viewer, url, Picurl, width, time) {
  const _this = this
  let promise = Cesium.GeoJsonDataSource.load(url)
  promise.then(function (dataSource) {
    viewer.dataSources.add(dataSource)
    _this.RoadPicEntities = dataSource.entities.values
    for (let i = 0; i < _this.RoadPicEntities.length; i++) {
      const entity = _this.RoadPicEntities[i]
      entity.polyline.width = width
      entity.polyline.material = new Cesium.Spriteline1MaterialProperty(time, Picurl)
    }
  })
}

/**
 * 好看的点特效集合
 * @param {*} viewer 
 * @param {Array} datas 需要展示的数据
 * @param {Object} effectsManager 效果集合管理控制
 * @param {*} vm 
 */
CesiumGlobe.prototype.pointEffectArray = function (viewer, datas, effectsManager, vm) {
  const _this = this;
  if (datas) {
    datas.forEach((element, index) => {
      _this.addPointEffect(viewer, effectsManager, element, index)
    })
  }
}

/**
 * 新增一个点特效
 * @param {*} viewer 
 * @param {Object} effectsManager 效果集合管理控制
 * @param {Object} ele 点数据
 * @param {Number} index 
 */
CesiumGlobe.prototype.addPointEffect = function (viewer, effectsManager, ele, index) {
  let curEntityC = null
  let pe = [ele.lon, ele.lat, ele.height]
  let ext;
  switch (ele.effect_type) {
    // 圆扩散
    case 'CircleDiffusion':
      curEntityC = new effectsManager.EllipsoidFade(
        viewer,
        'effect-set-config' + ele.effect_type + index
      )
      curEntityC.add(pe, ele.color, ele.radius, ele.duration)
      break
      // 线圈发光扩散
    case 'Scanline':
      curEntityC = new effectsManager.Scanline(
        viewer,
        'effect-set-config' + ele.effect_type + index
      )
      curEntityC.add(pe, ele.color, ele.radius, ele.duration)
      break
      // 水波纹
    case 'CircleWave':
      curEntityC = new effectsManager.CircleWave(
        viewer,
        'effect-set-config' + ele.effect_type + index
      )
      ext = JSON.parse(ele.ext)
      curEntityC.add(pe, ele.color, ele.radius, ele.duration, false, ext.waveCount)
      break
      // 六边形扩散
    case 'HexagonSpread':
      curEntityC = new effectsManager.HexagonSpread(
        viewer,
        'effect-set-config' + ele.effect_type + index
      )
      curEntityC.add(pe, ele.color, ele.radius, ele.duration)
      break
      // 雷达圆扫
    case 'CircleScan':
      curEntityC = new effectsManager.RaderScan(
        viewer,
        'effect-set-config' + ele.effect_type + index
      )
      ext = JSON.parse(ele.ext)
      curEntityC.add(pe, ele.color, ele.radius, ext.step)
      break
      // 墙推扩散
    case 'SpreadWall':
      curEntityC = new effectsManager.SpreadWall(
        viewer,
        'effect-set-config' + ele.effect_type + index
      )
      ext = JSON.parse(ele.ext)
      curEntityC.add(
        pe,
        ele.color,
        ele.radius,
        ele.duration,
        ext.height,
        ext.edgeCount
      )
      break
    default:
  }
  if (curEntityC) {
    this.EffectsList.push(curEntityC)
  }
}

/**
 * Primitive 加载模型List
 * @param {*} viewer 
 * @param {Array} datas 模型列表所需数据
 * @param {*} vm
 */
CesiumGlobe.prototype.pimitiveModelList = function (viewer, datas, vm) {
  const _this = this;

  // 先清空集合
  // viewer.scene.primitives.removeAll();
  CesiumGlobeData.primitiveModelList = [];

  if (datas) {
    datas.forEach((element, index) => {
      let points = {
        id: 'pimitiveModelList' + element.id,
        lon: element.positionLon,
        lat: element.positionLat,
        height: element.height,
        heading: element.latRotation,
        pitch: 0,
        roll: 0,
        colorMode: element.colorModelist, // 颜色模式 HIGHLIGHT高亮\MIX混合\REPLACE替换
        uri: element.url,
        scale: element.scale, // 大小
        rotateSpeed: element.rotateSpeed, // 转速
        modelColor: element.color, // 效果颜色
        minimumPixelSize: element.minimumPixelSize, // 模型最小以多少像素显示
        duration: element.duration // 旋转延迟ms
      }
      let options = {}
      window.CesiumGlobeData.primitiveModelList.push(_this.addModels(viewer, points, options));
    })
  }
}

/**
 * 添加模型
 * @param {*} viewer 
 * @param {*} points 点集
 * @param {*} options 模型配置
 * @returns 
 */
CesiumGlobe.prototype.addModels = function (viewer, points, options) {
  const _this = this
  const primitives = viewer.scene.primitives
  let id, model, toRadians, position, modelMatrix, hpr, modelColor
  toRadians = Cesium.Math.toRadians
  let value = points
  id = value.id ? value.id : 'pimitiveModel_0'
  _this.ModelId = id
  modelColor = new Cesium.Color.fromCssColorString(
    value.modelColor || options.modelColor || 'rgba(0,255,255,0.5)'
  ) // 模型颜色
  position = Cesium.Cartesian3.fromDegrees(
    value.lon,
    value.lat,
    value.height
  )
  hpr = new Cesium.HeadingPitchRoll(
    toRadians(value.heading),
    toRadians(value.pitch),
    toRadians(value.roll)
  )
  modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
    position,
    hpr
  )
  model = primitives.add(
    Cesium.Model.fromGltf({
      id: id,
      url: value.uri,
      scale: value.scale,
      modelMatrix: modelMatrix,
      color: modelColor,
      colorBlendMode: Cesium.ColorBlendMode.MIX,
      maximumScale: 5000,
      minimumPixelSize: value.minimumPixelSize || 20,
      scene: viewer.scene,
      heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND
    })
  )
  this.changePrimitiveColorBlendMode(viewer, id, value.colorMode)
  model.attributes = value
  model.heading = value.heading
  model.readyPromise.then(function (model) {
    const setIntervalId = setInterval(() => {
      model.heading += Cesium.Math.toRadians(model.attributes.rotateSpeed || 0)
      model.modelMatrix = _this._changeModelMatrix(model)
    }, value.duration)
    model.setIntervalId = setIntervalId // 用于清除定时器
  })
  return model
}

/**
 * 更改模型的Matrix
 * @param {*} model 模型
 * @returns 
 */
CesiumGlobe.prototype._changeModelMatrix = function (model) {
  // model.heading += Cesium.Math.toRadians(model.attributes.rotateSpeed || 0)
  const pitch = Cesium.defaultValue(model.attributes.pitch, 0.0)
  const roll = Cesium.defaultValue(model.attributes.roll, 0.0)
  const hpr = new Cesium.HeadingPitchRoll(model.heading, pitch, roll)

  const position = Cesium.Cartesian3.fromDegrees(
    model.attributes.lon,
    model.attributes.lat,
    model.attributes.height
  )
  const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
    position,
    hpr
  )
  return modelMatrix
}

/**
 * 更改Primitive的颜色混合模式
 * @param {*} viewer 
 * @param {*} id 
 * @param {*} mode 模型
 */
CesiumGlobe.prototype.changePrimitiveColorBlendMode = function (viewer, id, mode) {
  let primitive = this.findPrimitiveById(viewer, id)
  if (primitive) {
    let modeA = Cesium.ColorBlendMode.HIGHLIGHT
    switch (mode) {
      case 'HIGHLIGHT':
        modeA = Cesium.ColorBlendMode.HIGHLIGHT
        break
      case 'MIX':
        modeA = Cesium.ColorBlendMode.MIX
        break
      case 'REPLACE':
        modeA = Cesium.ColorBlendMode.REPLACE
        break
      default:
    }
    primitive.colorBlendMode = modeA
  }
}

/**
 * 根据id查找Primitive
 * @param {*} viewer 
 * @param {*} id 
 * @returns 
 */
CesiumGlobe.prototype.findPrimitiveById = function (viewer, id) {
  const primitives = viewer.scene.primitives._primitives
  let primitive = null
  primitives.forEach((value) => {
    if (value.id === id) {
      primitive = value
    }
  })
  return primitive
}

/**
 * 根据id，控制单个primitive的显隐
 * @param {*} viewer 
 * @param {*} id 
 * @param {Boolean} flag 
 */
CesiumGlobe.prototype.isShowPrimitiveById = function (viewer, id, flag) {
  const primitives = viewer.scene.primitives
  let primitive = null
  primitives.forEach((value) => {
    if (value.id === id) {
      primitive = value
    }
  })
  if (primitive) {
    primitive.show = flag
  } else {
    console.log(`不存在id为${id}的primitive`)
  }
}

/**
 * 控制primitiveCollection的显隐
 * @param {Array} primitiveCollection 
 * @param {Boolean} flag 
 */
CesiumGlobe.prototype.changePrimitiveVisibility = function (primitiveCollection, flag) {
  primitiveCollection.forEach((value) => {
    value.show = flag
  })
}

/**
 * 根据id，删除单个pimitive
 * @param {*} viewer 
 * @param {*} id 
 */
CesiumGlobe.prototype.delPrimitiveById = function (viewer, id) {
  const primitives = viewer.scene.primitives
  let primitive = null
  primitives.forEach((value) => {
    if (value.id === id) {
      primitive = value
    }
  })
  if (primitive) {
    // 清除模型绑定的定时器
    if (primitive.setIntervalId) clearInterval(primitive.setIntervalId)
    viewer.scene.primitives.remove(primitive)
  } else {
    console.log(`不存在id为${id}的primitive`)
  }
}

/**
 * 删除primitiveCollection
 * @param {*} viewer 
 * @param {Array} primitiveCollection 
 */
CesiumGlobe.prototype.delPrimitiveCollection = function (viewer, primitiveCollection) {
  primitiveCollection.forEach((value) => {
    // 清除模型绑定的定时器
    if (value.setIntervalId) clearInterval(value.setIntervalId)
    viewer.scene.primitives.remove(value)
  })
}

/**
 * 为实体添加剖面
 * @param {已创建完成的实体对象} tileset 
 * @param {剖面的相关参数，可传空} clippingPlanesOption 
 * @returns 剖面集（用于修改法线,使用方法见函数内部）
 */
CesiumGlobe.prototype.addClippingPlanes = function (tileset, clippingPlanesOption) {
  const clippingPlanes = new Cesium.ClippingPlaneCollection({
    planes: [
      new Cesium.ClippingPlane(
        new Cesium.Cartesian3(0.0, 0.0, -1.0), // 相对于模型中心的点位，决定了剖切方向
        5.0 // 离法线距离
      ),
    ],
    edgeColor: Cesium.Color.WHITE,
    edgeWidth: 1,
    ...clippingPlanesOption,
  });
  tileset.clippingPlanes = clippingPlanes;
  return clippingPlanes;

  // 动态修改法线离实体中心距离
  // clippingPlanes.planes中的每个元素对应一个剖面 
  // this 指向vue 与视图联动
  // 可直接修改 clippingPlanes.get(i).distance = this.distance;
  // 显示剖面时 创建的面实体的plane.plane属性对应clippingPlanes.planes中的一个元素
}

/**
 * 立方体区域单体化
 * @param {} viewer 
 * @param {立方体长} length 
 * @param {立方体宽} width 
 * @param {立方体高} height 
 * @param {离地距离} baseHeight 
 * @param {立方体中心点} Cartesian3 
 * @param {高亮颜色} Color 
 * @returns 立方体
 */
CesiumGlobe.prototype.addBoxClassificationPrimitive = function (
  viewer,
  length = 80.0,
  width = 80.0,
  height = 4.0,
  baseHeight = 0,
  Cartesian3,
  Color
) {
  let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
    Cartesian3
  );
  let hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(
    new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(-6), 0.0, 0.0)
  );
  let hpr = Cesium.Matrix4.fromRotationTranslation(
    hprRotation,
    new Cesium.Cartesian3(0.0, 0.0, baseHeight) //楼层高度上的偏移
  );
  Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix);
  return viewer.scene.primitives.add(
    new Cesium.ClassificationPrimitive({
      geometryInstances: new Cesium.GeometryInstance({
        geometry: Cesium.BoxGeometry.fromDimensions({
          vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
          dimensions: new Cesium.Cartesian3(length, width, height),
        }),
        modelMatrix: modelMatrix,
        attributes: {
          color: Cesium.ColorGeometryInstanceAttribute.fromColor(
            // new Cesium.Color(1.0, 0.0, 0.0, 0.8) //设置高亮颜色
            Color
          ),
          show: new Cesium.ShowGeometryInstanceAttribute(true), //设置初始化后是否显示
        },
      }),
      classificationType: Cesium.ClassificationType.CESIUM_3D_TILE, //只绘制在3dtiles上
    })
  );
}

/**
 * 多边形区域单体化
 * @param {*} viewer 
 * @param {[lng,lat,h,lng,lat,h]} polygon 
 * @param {顶部离地距离} extrudedHeight 
 * @param {离地距离} height 
 * @param {高亮颜色} Color 
 * @returns 多边形
 */
CesiumGlobe.prototype.addPolygonClassificationPrimitive = function (
  viewer,
  polygon,
  extrudedHeight,
  height,
  Color
) {
  return viewer.entities.add({
    polygon: {
      hierarchy: new Cesium.PolygonHierarchy(
        Cesium.Cartesian3.fromDegreesArrayHeights(polygon)
      ),
      classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
      material: Color,
      extrudedHeight, // 顶部离地距离
      height, // 离地距离
    },
  });
}

/**
 * 添加图标点（图片）
 * @param {*} viewer 
 * @param {*} datas 所有点位数据
 * @param {*} type 自定义点位类型，如摄像头：camera
 * @param {*} vm 
 */
CesiumGlobe.prototype.addBillboard = function (viewer, datas, type, vm) {
  let _this = this;
  datas = datas.filter((element, index) => {
    return element.position.lng && element.position.lat && element.id;
  });
  datas.forEach((element, index) => {
    _this.addOneBillboard(viewer, type, element, index, vm)
  });
}

/**
 * 添加一个图标点（图片）
 * @param {*} viewer 
 * @param {*} type 自定义点位类型，如摄像头：camera
 * @param {*} element 某一个点位数据
 * @param {*} index 
 * @param {*} vm 
 */
CesiumGlobe.prototype.addOneBillboard = function (viewer, type, element, index, vm) {
  let entity = {
    id: "billboard_" + type + "_" + element.id, // 唯一性
    name: element.name ? element.name : index,
    show: element.isShow === undefined ? true : element.isShow,
    position: Cesium.Cartesian3.fromDegrees(
      element.position.lng,
      element.position.lat,
      element.position.height
    ),
    properties: element.properties,
  };
  let item = element.properties;
  let opt = element.billboard;
  let billboard = {
    // 是否显示
    show: true,
    // 图像地址，URI或Canvas的属性
    image: item.defaultImage.base64,
    // 应用于图像的统一比例。比例大于会1.0放大标签，而比例小于会1.0缩小标签。
    scale: opt && opt.scale ? opt.scale : 1.0,
    // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
    pixelOffset: opt && opt.pixelOffset ? opt.pixelOffset : new Cesium.Cartesian2(0, 0),
    // 眼睛看到的偏移量
    eyeOffset: opt && opt.eyeOffset ? opt.eyeOffset : new Cesium.Cartesian3(0, 0, 0),
    // 相对于坐标的水平位置 CENTER、LEFT、RIGHT
    horizontalOrigin: opt && opt.horizontalOrigin ? opt.horizontalOrigin : Cesium.HorizontalOrigin.CENTER,
    // 相对于坐标的垂直位置 CENTER、BOTTOM、BASELINE、TOP
    verticalOrigin: opt && opt.verticalOrigin ? opt.verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
    // 相对于坐标的高度 NONE、CLAMP_TO_GROUND、RELATIVE_TO_GROUND
    heightReference: opt && opt.heightReference ? opt.heightReference : Cesium.HeightReference.NONE,
    // 设置颜色和透明度
    color: opt && opt.color ? opt.color : Cesium.Color.WHITE.withAlpha(1),
    // 旋转度
    rotation: opt && opt.rotation ? opt.rotation : 0,
    // 旋转轴
    alignedAxis: opt && opt.alignedAxis ? opt.alignedAxis : Cesium.Cartesian3.ZERO,
    // 大小是否以米为单位
    sizeInMeters: opt && opt.sizeInMeters ? opt.sizeInMeters : false,
    // 高度（以像素为单位）
    height: item.defaultImage.height,
    // 宽度（以像素为单位）
    width: item.defaultImage.width,
    // 定义与相机的距离，显示在距相机的距离在多少区间内是可以显示的
    distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
      opt && opt.distanceDisplayCondition ? opt.distanceDisplayCondition[0] : 0,
      opt && opt.distanceDisplayCondition ? opt.distanceDisplayCondition[1] : 50000000
    ),
    // 设置圆点深度检测阈值，解决Cesium billboard被3dtiles遮挡问题
    disableDepthTestDistance: Number.POSITIVE_INFINITY,
  };
  entity.billboard = billboard;
  viewer.entities.add(entity);
}

/**
 * 添加Label
 * @param {*} viewer 
 * @param {*} datas 所有点位数据
 * @param {*} type 自定义点位类型，如摄像头：camera
 * @param {*} vm 
 */
CesiumGlobe.prototype.addLabel = function (viewer, datas, type, vm) {
  let _this = this;
  datas = datas.filter((element, index) => {
    return element.position.lng && element.position.lat && element.id;
  });
  datas.forEach((element, index) => {
    _this.addOneLabel(viewer, type, element, index, vm)
  });
}

/**
 * 添加一个Label
 * @param {*} viewer 
 * @param {*} type 自定义点位类型，如摄像头：camera
 * @param {*} element 某一个点位数据
 * @param {*} index 
 * @param {*} vm 
 */
CesiumGlobe.prototype.addOneLabel = function (viewer, type, element, index, vm) {
  let entity = {
    id: "label_" + type + "_" + element.id, // 唯一性
    name: element.name ? element.name : index,
    show: element.isShow === undefined ? true : element.isShow,
    position: Cesium.Cartesian3.fromDegrees(
      element.position.lng,
      element.position.lat,
      element.position.height
    ),
    properties: element.properties,
  };
  let item = element.properties;
  let opt = element.label;
  let label = {
    // 是否显示
    show: true,
    // 指定文本。支持显式换行符 '\n'
    text: element.name ? element.name : index + '',
    // 指定 CSS 字体
    font: opt && opt.font ? opt.font : "18px sans-serif",
    // 样式 FILL、OUTLINE、FILL_AND_OUTLINE
    style: opt && opt.style ? opt.style : Cesium.LabelStyle.FILL_AND_OUTLINE,
    // 应用于图像的统一比例。比例大于会1.0放大标签，而比例小于会1.0缩小标签。
    scale: opt && opt.scale ? opt.scale : 1.0,
    // 背景的可见性
    showBackground: opt && opt.showBackground ? opt.showBackground : true,
    // 背景色
    backgroundColor: opt && opt.backgroundColor ? opt.backgroundColor : new Cesium.Color(10 / 255, 17 / 255, 47 / 255, 0.4),
    // 以像素为单位指定水平和垂直背景填充
    backgroundPadding: opt && opt.backgroundPadding ? opt.backgroundPadding : new Cesium.Cartesian2(7, 5),
    // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
    pixelOffset: opt && opt.pixelOffset ? opt.pixelOffset : new Cesium.Cartesian2(0, -item.defaultImage.height - 8),
    // 眼睛看到的偏移量
    eyeOffset: opt && opt.eyeOffset ? opt.eyeOffset : new Cesium.Cartesian3(0, 0, 0),
    // 相对于坐标的水平位置 CENTER、LEFT、RIGHT
    horizontalOrigin: opt && opt.horizontalOrigin ? opt.horizontalOrigin : Cesium.HorizontalOrigin.CENTER,
    // 相对于坐标的垂直位置 CENTER、BOTTOM、BASELINE、TOP
    verticalOrigin: opt && opt.verticalOrigin ? opt.verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
    // 相对于坐标的高度 NONE、CLAMP_TO_GROUND、RELATIVE_TO_GROUND
    heightReference: opt && opt.heightReference ? opt.heightReference : Cesium.HeightReference.NONE,
    // 指定填充颜色
    fillColor: opt && opt.fillColor ? opt.fillColor : Cesium.Color.WHITE.withAlpha(1),
    // 轮廓颜色
    outlineColor: opt && opt.outlineColor ? opt.outlineColor : Cesium.Color.WHITE.withAlpha(1),
    // 轮廓宽度
    outlineWidth: opt && opt.outlineWidth ? opt.outlineWidth : 2.0,
    // 定义与相机的距离，显示在距相机的距离在多少区间内是可以显示的
    distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
      opt && opt.distanceDisplayCondition ? opt.distanceDisplayCondition[0] : 0,
      opt && opt.distanceDisplayCondition ? opt.distanceDisplayCondition[1] : 50000000
    ),
    // 设置label深度检测阈值，解决Cesium label被3dtiles遮挡问题
    disableDepthTestDistance: Number.POSITIVE_INFINITY,
  };
  entity.label = label;
  viewer.entities.add(entity);
}

/**
 * 根据数据id获取Entity
 * @param {*} viewer 
 * @param {billboard \ label} plotType Entity的类型
 * @param {*} type 自定义点位类型，如摄像头：camera
 * @param {*} id 
 * @returns 
 */
CesiumGlobe.prototype.getEntityById = function (viewer, plotType, type, id) {
  if (id) {
    return viewer.entities.getById(plotType + "_" + type + "_" + id);
  } else {
    return undefined;
  }
}

/**
 * 根据ID删除Entity
 * @param {*} viewer 
 * @param {*} plotType Entity的类型
 * @param {*} type 自定义点位类型，如摄像头：camera
 * @param {*} id 
 * @returns 
 */
CesiumGlobe.prototype.removeEntityById = function (viewer, plotType, type, id) {
  if (id) {
    viewer.entities.removeById(plotType + "_" + type + "_" + id)
  } else {
    return false;
  }
}

/**
 * 根据entity（实体）对象删除
 * @param {*} viewer 
 * @param {Object} entity_obj 
 * @returns 
 */
CesiumGlobe.prototype.removeEntity = function (viewer, entity_obj) {
  if (entity_obj) {
    viewer.entities.remove(entity_obj)
  } else {
    return false;
  }
}

/**
 * 删除全部Entity
 * @param {*} viewer 
 */
CesiumGlobe.prototype.removeAllEntity = function (viewer) {
  viewer.entities.removeAll()
}

/**
 * 视频融合(视频墙)
 * @param {*} viewer 
 * @param {*} datas 所有需展示的视频数据
 * @param {*} vm 
 */
CesiumGlobe.prototype.addVideo = function (viewer, datas, vm) {
  let _this = this;
  datas.forEach((element, index) => {
    _this.addOneVideo(viewer, element, index, vm);
  });
}

/**
 * 添加一个视频
 * @param {*} viewer
 * @param {*} element 某一个视频数据
 * @param {*} index
 * @param {*} vm
 */
CesiumGlobe.prototype.addOneVideo = function (viewer, element, index, vm) {
  // 获取到视频元素
  let videoElement = document.getElementById(element.id);

  videoElement.play(); //开始播放
  viewer.showRenderLoopErrors = false;
  viewer.shouldAnimate = true;

  // 在cesium中创建一个模型来承载视频，使得视频可以在该处进行播放
  var instance = new Cesium.GeometryInstance({
    geometry: new Cesium.WallGeometry({
      // pArray: 墙面的左右2个点的经纬度和最大高度
      positions: Cesium.Cartesian3.fromDegreesArrayHeights(element.pArray),
      // 最低位置（最小高度）。单位米
      minimumHeights: element.minimumHeights ?
        element.minimumHeights : [1.0, 1.0],
    }),
  });
  // 将该材质设置为视频，并给与模型
  var material = Cesium.Material.fromType("Image");
  material.uniforms.image = videoElement; // 将材质设置为video元素
  viewer.scene.primitives.add(
    new Cesium.Primitive({
      geometryInstances: instance,
      appearance: new Cesium.MaterialAppearance({
        closed: false,
        material: material,
      }),
    })
  );
  
  // 需要时钟同步。不然没法做到隐藏video标签的同时还能播放视频
  // new Cesium.VideoSynchronizer({
  //   clock: viewer.clock,
  //   element: videoElement,
  // });
  // viewer.clock.shouldAnimate = true;
  
  // 隐藏video标签
  videoElement.style.display = "none";
}

/**
 * 其它的可用工具类
 */
CesiumGlobe.prototype.oTools = {
  colorRgb: colorRgb,
  colorRgbString: colorRgbString,
  colorRgb1: colorRgb1,
  colorRGB2Hex: colorRGB2Hex,
  evil: evil,
  getUrlKey: getUrlKey,
  getUrlParma: getUrlParma,
  string_to_name: string_to_name,
}





// =============================== 【其它工具类】===============================
/*
 * 颜色值处理通用函数
 */
// 输入16进制颜色值 转 rgb 的数组值 255
function colorRgb(inColor) {
  // 16进制颜色值的正则
  const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
  // 把颜色值变成小写
  let color = inColor.toLowerCase()
  if (reg.test(color)) {
    // 如果只有三位的值，需变成六位，如：#fff => #ffffff
    if (color.length === 4) {
      let colorNew = '#'
      for (let i = 1; i < 4; i += 1) {
        colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1))
      }
      color = colorNew
    }
    // 处理六位的颜色值，转为RGB
    const colorChange = []
    for (let i = 1; i < 7; i += 2) {
      colorChange.push(parseInt('0x' + color.slice(i, i + 2)))
    }
    return colorChange
  }
  return []
}
// 输入16进制颜色值 转 rgb 的字符 255
function colorRgbString(inColor) {
  let arr = colorRgb(inColor)
  return 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'
}
// 输入16进制颜色值 转 rgb 的数组值 0.0 - 1.0
function colorRgb1(inColor) {
  const colorChange = colorRgb(inColor)
  colorChange.forEach((ele, index) => {
    colorChange[index] = (ele / 255.0).toFixed(2)
  })
  return colorChange
}
// RGB转16进制(rgb2hex)
function colorRGB2Hex(color) {
  const rgb = color.split(',')
  if (rgb.length <= 1) {
    return color
  }
  const r = parseInt(rgb[0].split('(')[1])
  const g = parseInt(rgb[1])
  const b = parseInt(rgb[2].split(')')[0])
  const hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
  return hex
}
/**
 * 函数
 */
// 函数
function evil(fn) {
  const Fn = Function // 一个变量指向Function，防止有些前端编译工具报错
  // return new Fn('return ' + fn)()
  return new Fn('return ' + fn)()
}
// 获得get参数
function getUrlKey(name, url) {
  return (
    decodeURIComponent(
      (new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(url) || [
        null,
        '',
        null,
      ])[1].replace(/\+/g, '%20')
    ) || null
  )
}
// 获得格式化后的get参数
function getUrlParma(p, type = 'string') {
  let UrlParma = getUrlKey(p, window.location.href)
  if (type === 'int') {
    return parseInt(UrlParma)
  }
  if (type === 'float') {
    return parseFloat(UrlParma)
  }
  if (UrlParma && type === 'array') {
    UrlParma = UrlParma.split(',')
    return [
      parseFloat(UrlParma[0]),
      parseFloat(UrlParma[1]),
      parseFloat(UrlParma[2]),
    ]
  }
  return UrlParma
}
// 将一个字符串转换为变量名
function string_to_name(string) {
  let _name = 'let new_name = ' + string
  evil(_name)
  return _name
}